from datetime import datetime, timedelta
import backtrader as bt
from backtrader.utils import logger
import backtrader.brokers as brokermgt
import pytz
import os

tz = pytz.timezone('Asia/Shanghai')
class BaseLiveStrat(bt.Strategy):

    params = (
        # period for the fast Moving Average
        ('name', "BaseStrat"),
        ('asset_type', "spot"),
        ('account', "no"),
        ('max_amount', 100),  # 开仓最大金额
        ('max_size', 20),  # 最大仓位
        ('max_trade', 1),  # 小于等于0为无限制
        ('ordtype', bt.Order.ExecTypes[bt.Order.Market]),  # 委托单类型
        ('slippage', 0),  # 滑点
        ('token', ""),
        ("leverage", 1),
        ('enable', False),
        ('debug', False),
    )

    def __init__(self):
        super(BaseLiveStrat, self).__init__()
        self.run_count = 0
        # self.symbol = self.data._dataname
        # self.markets = self.broker.fetch_market(symbol=self.symbol)
        self.logger = logger.getLogger(__name__)
        self.alert = bt.utils.Alert(self.p.token, self.p.enable)
        # 交易次数, 统计提交开仓的次数
        self.trade_count = 0
        broker_proxy_cls = getattr(brokermgt, f"{self.broker.store.exchange.id.capitalize()}BrokerProxy")
        self.broker_proxy = broker_proxy_cls(self)
        self.start_flag = False
        self.live_ready = {}
        self.live_data = False

    def empty(self, value):
        return value < 10

    def setting_broker(self):
        for data in self.datas:
            pass

    def format_simple(self, data):
        if not data._name.endswith("USDT"):
            return data._name[0:data._name.index("USDT") + len("USDT")]
        return data._name

    def nextstart(self):
        # 加载已经存在的当前挂单
        if self.start_flag:
            return
        self.broker.load_opens_order(self)
        params = self.p.__dict__.copy()
        self.logger.info("*" * 77)
        for k, v in params.items():
            if "k" == "token":
                continue
            self.logger.info("参数: @@@@【%15s = %s(%s)】", k, v, type(v).__name__)
        self.logger.info("*" * 77)
        self.start_flag = True

    def check_open_amount(self, amount):
        if self.p.max_amount <= 0:
            return False, amount, amount

        if amount > self.p.max_amount:
            return True, self.p.max_amount, amount
        else:
            return False, amount, amount

    def send_msg(self, symbol, action, content, dt):
        _format = self.alert.format()
        params = {
            "name": self.p.name,
            "asset": symbol,
            "account": self.p.account,
            "action": action,
            "detail": content,
            "date": dt
        }
        self.alert.send(_format.format(**params))

    def allow_trade(self):
        if self.p.max_trade <= 0:
            return True
        return self.trade_count <= self.p.max_trade

    """
    买现货, 现货market买入的size是买多少钱
    """

    def buy_spot(self, data, exectype, size=None, price=None):
        return self.broker_proxy.buy_spot(data, exectype, size, price)

    """
    卖现货
    """

    def sell_spot(self, data, exectype, size=None, price=None):
        return self.broker_proxy.sell_spot(data, exectype, size, price)

    """
        止损单
        STOP_LOSS	quantity, stopPrice
        STOP_LOSS_LIMIT quantity, stopPrice, price
        币安交易所不支持stop_loss
        """

    def stop_loss_spot(self, data, exectype, size, price, trigger_px):
        return self.broker_proxy.stop_loss_spot(data, exectype, size, price, trigger_px)

    """
    合约多头开仓和空头平仓
    """
    def buy_future(self, data, exectype, size, order_price=None):
        return self.broker_proxy.buy_future(data, exectype, size, order_price)

    """
    合约空头开仓和多头平仓
    """
    def sell_future(self, data, exectype, size, order_price=None):
        return self.broker_proxy.sell_future(data, exectype, size, order_price)

    """
    合约止盈止损设置
    """
    def stop_profit_future(self, data, exectype, side, size, order_price, trigger_px):
        return self.broker_proxy.stop_profit_future(data, exectype, side, size, order_price, trigger_px)

    """
    市价止盈单
    STOP_LOSS_LIMIT	timeInForce, quantity, price, stopPrice
    """

    def take_profit_spot(self, symbol, size, stop_price):
        pass

    """
    限价止盈单
    STOP_LOSS_LIMIT	timeInForce, quantity, price, stopPrice
    """

    def take_profit_limit_spot(self, symbol, size, stop_price, price):
        pass


    def log(self, label, symbol, txt, bar_dt=None):
        ''' Logging function fot this strategy'''
        if bar_dt is None:
            bar_dt = datetime.today()
        if isinstance(bar_dt, float) or isinstance(bar_dt, int):
            bar_dt0 = datetime.fromtimestamp(int(bar_dt / 1000), tz=tz)
            if bar_dt0.year <= 1980:
                bar_dt = bt.num2date(bar_dt) + timedelta(hours=8)
            else:
                bar_dt = bar_dt0
        dt_str = bar_dt.strftime("%Y-%m-%d %H:%M:%S")
        self.logger.info("[**%15s**], [bar: %s], [symbol: %s], [%s]", label, dt_str, symbol, txt)

    def prenext(self):
        '''
        从第一条数据开始, 在miniperiod 阶段中调用, 当满足miniperiod时候就不调用了
        比如ma(3), 前两条调用prenext, 第3条数据的时候调用next
        '''
        self.run_count += 1
        for i, d in enumerate(self.datas):
            if len(d) > 0:
                dtstr = d.datetime.datetime() + timedelta(hours=8)
                text = "Previous Load Data, run count: [%s]" % self.run_count
                # self.log("PRENEXT DATA", d._name, text, dtstr)

    def indicator_print(self):
        pass

    def notify_data(self, data, status, *args, **kwargs):
        symbol = data._name
        text = 'Data Status: %s' % data._getstatusname(status)
        self.log("STATUS NOTIFY", symbol, text)

        if data._getstatusname(status) == 'LIVE':
            self.live_ready[data._name] = True
        else:
            self.live_ready[data._name] = False

        for data in self.datas:
            if data._name not in self.live_ready:
                self.live_ready = False
                break
            elif not self.live_ready[data._name]:
                self.live_data = False
                break
            else:
                self.live_data = True

        if self.live_data:
            self.data_print()
            self.indicator_print()


    #  header = "strat,account,symbol,bar_time,order_time,trade_time,id,side,
    #   type,trigger_px,order_price,order_size,trade_price,trade_size,comm\n"
    def add_recorder(self, order):
        symbol = order.data._name
        if order.status in [order.Completed]:
            record = [self.p.name, self.p.account, symbol] + self.broker_proxy.parse_close_trader(order)
            record = list(map(lambda x: "" if x is None else str(x), record))
            self.writer.write(",".join(record))
            self.writer.write("\r\n")
            self.writer.flush()

    def notify_order(self, order):
        symbol = order.data._name
        cash, value = self.broker.getcash(), self.broker.getvalue(self.datas)
        text = "Cash : %.2f, Value: %.2f" % (cash, value)
        self.log("ACCOUNT NOTIFY", symbol, text, order.created.dt)

        if order.status == order.Submitted:
            self.log("ORDER NOTIFY", symbol, "Order Submitted, [Id: %s]" % order.id, order.created.dt)
            return
        if order.status == order.Accepted:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            text = "Order Submitted Completed, [Id: %s, Type: %s, TriggerPrice: %s, Price: %s, Size: %s]" % \
                   (order.id, order.getordername(), order.plimit, order.price, order.size)
            self.log("ORDER NOTIFY", symbol, text, order.created.dt)
            return

        if order.status == order.Trigger:
            if order.trigger_loss_price:
                text = "Stop Order Trigger Completed, [Id: %s, Type: %s, Loss_trigger: %s, Loss_Px: %s, Size: %s]" % \
                       (order.id, order.getordername(), order.trigger_loss_price, order.loss_price, order.size)
            if order.trigger_profit_price:
                text = "Stop Order Trigger Completed, [Id: %s, Type: %s, Profit_Trigger: %s, Profit_Px: %s, Size: %s]" % \
                       (order.id, order.getordername(), order.trigger_profit_price, order.profit_price, order.size)
            if order.trigger_loss_price and order.trigger_profit_price:
                text = "Stop Order Trigger Completed, [Id: %s, Type: %s, Loss_trigger: %s, Loss_Px: %s, Profit_Trigger: %s, Profit_Px: %s, " \
                       "Size: %s]" % (order.id, order.getordername(), order.trigger_loss_price, order.loss_price,
                                      order.trigger_profit_price, order.profit_price, order.size)

            self.log("ORDER NOTIFY", symbol, text, order.trigger_time)
            return

        if order.status in [order.Expired]:
            self.log("ORDER NOTIFY", symbol, "Order Expired, [Id: %s]" % order.id, order.created.dt)

        elif order.status in [order.Completed]:
            if order.isbuy():
                text = "Order Buy Completed, [Id: %s, Type: %s, Price: %.6f, Size: %s, Comm %s]" % \
                       (order.id, order.getordername(),
                        order.executed.price if order.executed.price else 0, order.executed.size, order.executed.comm)
                self.log("ORDER NOTIFY", symbol, text, order.executed.dt)

            else:  # Sell
                text = "Order Sell Completed, [Id: %s, Type: %s, Price: %.6f, Size: %s, Comm %s]" % \
                       (order.id, order.getordername(),
                        order.executed.price if order.executed.price else 0, order.executed.size, order.executed.comm)
                self.log("ORDER NOTIFY", symbol, text, order.executed.dt)
            # self.add_recorder(order)
        elif order.status in [order.Canceled]:
            text = "Order Cancel Completed, [Id: %s, Type: %s, Price: %.6f, Size: %s, pricelimit %s]" % \
                   (order.id, order.getordername(), order.price if order.price else 0, order.size,
                    order.pricelimit)
            self.log("ORDER NOTIFY", symbol, text, order.created.dt)
        elif order.status in [order.Margin]:
            self.log("ORDER NOTIFY", symbol, "Order Failed, not enough cash. [Id: %s]" % order.id, order.created.dt)

    def data_print(self):
        for d in self.datas:
            bar_dt0 = d.datetime.datetime() + timedelta(hours=8)
            open0, high0, low0, close0, vol0, amount0 = d.open[0], d.high[0], d.low[0], d.close[0], d.volume[0], d.amount[0]
            text = "Live: %s, O: %10.4f, H: %10.4f, L: %10.4f, C: %10.4f, V: %10.4f, A: %10.4f" % \
                   (self.live_data, open0, high0, low0, close0, vol0, amount0)
            self.log("DATA NOTIFY", d._name, text, bar_dt0)